/*
 * Copyright (C) 2017 Jared Rummler
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.jaredrummler.colorpicker;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcelable;

import androidx.core.graphics.ColorUtils;
import androidx.core.view.GravityCompat;
import androidx.core.view.ViewCompat;
import androidx.preference.CMN;

import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.Toast;

import com.jaredrummler.android.colorpicker.R;

import java.util.Locale;

/**
 * This class draws a panel which which will be filled with a color which can be set. It can be used to show the
 * currently selected color which you will get from the {@link ColorPickerView}.
 */
public class ColorPanelView extends View {

	private final static int DEFAULT_BORDER_COLOR = 0xFF6E6E6E;

	private Drawable alphaPattern;
	private Paint borderPaint;
	private Paint colorPaint;
	private Paint alphaPaint;
	private Rect drawingRect;
	private Rect colorRect;
	private RectF centerRect = new RectF();

	/* The width in pixels of the border surrounding the color panel. */
	private int borderWidthPx;
	private int borderColor = DEFAULT_BORDER_COLOR;
	private int color = Color.BLACK;
	private double luminance = 0;
	private int shape;

	public ColorPanelView(Context context) {
		this(context, null);
	}

	public ColorPanelView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public ColorPanelView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context, attrs);
	}

	@Override public Parcelable onSaveInstanceState() {
		Bundle state = new Bundle();
		state.putParcelable("instanceState", super.onSaveInstanceState());
		state.putInt("color", color);
		return state;
	}

	@Override public void onRestoreInstanceState(Parcelable state) {
		if (state instanceof Bundle) {
			Bundle bundle = (Bundle) state;
			color = bundle.getInt("color");
			state = bundle.getParcelable("instanceState");
		}
		super.onRestoreInstanceState(state);
	}

	private void init(Context context, AttributeSet attrs) {
		TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ColorPanelView);
		shape = a.getInt(R.styleable.ColorPanelView_cpv_colorShape, ColorShape.CIRCLE);
		borderColor = a.getColor(R.styleable.ColorPanelView_cpv_borderColor, DEFAULT_BORDER_COLOR);
		a.recycle();
		if (borderColor == DEFAULT_BORDER_COLOR) {
			// If no specific border color has been set we take the default secondary text color as border/slider color.
			// Thus it will adopt to theme changes automatically.
			final TypedValue value = new TypedValue();
			TypedArray typedArray = context.obtainStyledAttributes(value.data, new int[]{android.R.attr.textColorSecondary});
			borderColor = typedArray.getColor(0, borderColor);
			typedArray.recycle();
		}
		borderWidthPx = DrawingUtils.dpToPx(context, 1);
		borderPaint = new Paint();
		borderPaint.setAntiAlias(true);
		colorPaint = new Paint();
		colorPaint.setAntiAlias(true);
		if (shape == ColorShape.CIRCLE) {
			Bitmap bitmap = ((BitmapDrawable) context.getResources().getDrawable(R.drawable.cpv_alpha)).getBitmap();
			alphaPaint = new Paint();
			alphaPaint.setAntiAlias(true);
			BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
			alphaPaint.setShader(shader);
		}
	}

	@Override protected void onDraw(Canvas canvas) {
		borderPaint.setColor(borderColor);
		colorPaint.setColor(color);
		if (shape == ColorShape.SQUARE) {
			if (borderWidthPx > 0) {
				canvas.drawRect(drawingRect, borderPaint);
			}
			if (alphaPattern != null) {
				alphaPattern.draw(canvas);
			}
			canvas.drawRect(colorRect, colorPaint);
		} else if (shape == ColorShape.CIRCLE) {
			final int outerRadius = getMeasuredWidth() / 2;
			if (borderWidthPx > 0) {
				//canvas.drawCircle(getMeasuredWidth() / 2,
				//		getMeasuredHeight() / 2,
				//		outerRadius,
				//		borderPaint);
			}
			boolean isShrinked=Color.alpha(color) < 255 || luminance>0.8;

			if (isShrinked) {
				colorPaint.setColor(0xFF000000|color);
				if(luminance>0.8){
					int val = (int) (0x22*(luminance-0.8)*5);
					colorPaint.setColor(colorPaint.getColor()-Color.argb(0,val,val,val));
				}
				//border
				canvas.drawCircle(getMeasuredWidth() / 2,
						getMeasuredHeight() / 2,
						outerRadius,
						colorPaint);
				//cut out
				canvas.drawCircle(getMeasuredWidth() / 2,
						getMeasuredHeight() / 2,
						outerRadius - borderWidthPx, alphaPaint);
			}

			//raw color
			colorPaint.setColor(color);
			canvas.drawCircle(getMeasuredWidth() / 2,
					getMeasuredHeight() / 2,
					outerRadius - (isShrinked?borderWidthPx:0),
					colorPaint);
		}
	}

	@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		if (shape == ColorShape.SQUARE) {
			int width = MeasureSpec.getSize(widthMeasureSpec);
			int height = MeasureSpec.getSize(heightMeasureSpec);
			setMeasuredDimension(width, height);
		} else if (shape == ColorShape.CIRCLE) {
			super.onMeasure(widthMeasureSpec, widthMeasureSpec);
			setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
		} else {
			super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		}
	}

	@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		if (shape == ColorShape.SQUARE  ) {
			drawingRect = new Rect();
			drawingRect.left = getPaddingLeft();
			drawingRect.right = w - getPaddingRight();
			drawingRect.top = getPaddingTop();
			drawingRect.bottom = h - getPaddingBottom();
			setUpColorRect();
		}
	}

	private void setUpCenterRect() {
		final Rect dRect = drawingRect;
		int left = dRect.left + borderWidthPx;
		int top = dRect.top + borderWidthPx;
		int bottom = dRect.bottom - borderWidthPx;
		int right = dRect.right - borderWidthPx;
		centerRect = new RectF(left, top, right, bottom);
	}

	private void setUpColorRect() {
		final Rect dRect = drawingRect;
		int left = dRect.left + borderWidthPx;
		int top = dRect.top + borderWidthPx;
		int bottom = dRect.bottom - borderWidthPx;
		int right = dRect.right - borderWidthPx;
		colorRect = new Rect(left, top, right, bottom);
		alphaPattern = new AlphaPatternDrawable(DrawingUtils.dpToPx(getContext(), 4));
		alphaPattern.setBounds(Math.round(colorRect.left),
				Math.round(colorRect.top),
				Math.round(colorRect.right),
				Math.round(colorRect.bottom));
	}

	/**
	 * Set the color that should be shown by this view.
	 *
	 * @param color
	 *     the color value
	 */
	public void setColor(int color) {
		this.color = color;
		this.luminance = ColorUtils.calculateLuminance(color);
		invalidate();
	}

	/**
	 * Get the color currently show by this view.
	 * @return the color value
	 */
	public int getColor() {
		return color;
	}

	public double getLuminance() {
		return luminance;
	}

	/**
	 * Set the color of the border surrounding the panel.
	 *
	 * @param color
	 *     the color value
	 */
	public void setBorderColor(int color) {
		borderColor = color;
		invalidate();
	}

	/**
	 * @return the color of the border surrounding the panel.
	 */
	public int getBorderColor() {
		return borderColor;
	}

	/**
	 * Set the shape.
	 *
	 * @param shape
	 *     Either {@link ColorShape#SQUARE} or {@link ColorShape#CIRCLE}.
	 */
	public void setShape(@ColorShape int shape) {
		this.shape = shape;
		invalidate();
	}

	/**
	 * Get the shape
	 *
	 * @return Either {@link ColorShape#SQUARE} or {@link ColorShape#CIRCLE}.
	 */
	@ColorShape public int getShape() {
		return shape;
	}

	/**
	 * Show a toast message with the hex color code below the view.
	 */
	public void showHint() {
		final int[] screenPos = new int[2];
		final Rect displayFrame = new Rect();
		getLocationOnScreen(screenPos);
		getWindowVisibleDisplayFrame(displayFrame);
		final Context context = getContext();
		final int width = getWidth();
		final int height = getHeight();
		final int midy = screenPos[1] + height / 2;
		int referenceX = screenPos[0] + width / 2;
		if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_LTR) {
			final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
			referenceX = screenWidth - referenceX; // mirror
		}
		StringBuilder hint = new StringBuilder("#");
		if (Color.alpha(color) != 255) {
			hint.append(Integer.toHexString(color).toUpperCase(Locale.ENGLISH));
		} else {
			hint.append(String.format("%06X", 0xFFFFFF & color).toUpperCase(Locale.ENGLISH));
		}
		Toast cheatSheet = Toast.makeText(context, hint.toString(), Toast.LENGTH_SHORT);
		if (midy < displayFrame.height()) {
			// Show along the top; follow action buttons
			cheatSheet.setGravity(Gravity.TOP | GravityCompat.END, referenceX,
					screenPos[1] + height - displayFrame.top);
		} else {
			// Show along the bottom center
			cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
		}
		cheatSheet.show();
	}

}
